package com.zhiyuan.manager;

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 *
 *
 * @author zhiyuan
 * @date 2025/10/25
 */
public class TwoLevelCache implements Cache {

    private final String name;
    private final Cache l1;
    private final Cache l2;

    /** 失效发布器：cacheName + key (或 null 表示全清) */
    private final InvalidatePublisher publisher;

    @FunctionalInterface
    interface InvalidatePublisher {
        void publish(String cacheName, Object keyOrWildcard);
    }

    TwoLevelCache(String name, Cache l1, Cache l2, InvalidatePublisher publisher) {
        this.name = name;
        this.l1 = l1;
        this.l2 = l2;
        this.publisher = publisher;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Object getNativeCache() {
        Map<String, Object> nativeView = new HashMap<>(2);
        nativeView.put("l1", l1.getNativeCache());
        nativeView.put("l2", l2.getNativeCache());
        return nativeView;
    }

    // ---- GET ----

    @Override
    public ValueWrapper get(Object key) {
        // 1) L1
        ValueWrapper v = l1.get(key);
        if (v != null && v.get() != null) return v;

        // 2) L2
        v = l2.get(key);
        if (v != null && v.get() != null) {
            // 回填 L1
            l1.put(key, v.get());
            return v;
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(Object key, Class<T> type) {
        ValueWrapper vw = get(key);
        if (vw == null) return null;
        Object val = vw.get();
        if (val == null) return null;
        if (type != null && !type.isInstance(val)) {
            throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + val.getClass());
        }
        return (T) val;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(Object key, Callable<T> valueLoader) {
        // 快路径：L1
        ValueWrapper v = l1.get(key);
        if (v != null && v.get() != null) return (T) v.get();

        // 次路径：L2
        v = l2.get(key);
        if (v != null && v.get() != null) {
            Object val = v.get();
            l1.put(key, val);
            return (T) val;
        }

        // 回源：加载并同时写入 L2 与 L1
        try {
            T loaded = valueLoader.call();
            if (loaded != null) {
                l2.put(key, loaded);
                l1.put(key, loaded);
            } else {
                // 可选：对空值策略，这里直接不写入；如需缓存空值，可在此实现
            }
            return loaded;
        } catch (Exception ex) {
            throw new ValueRetrievalException(key, valueLoader, ex);
        }
    }

    // ---- PUT ----

    @Override
    public void put(Object key, Object value) {
        // 写两边：先 L2 再 L1，避免并发下 L1 抢先被读到旧值
        l2.put(key, value);
        l1.put(key, value);
        // 可选广播：通知其它节点清 L1（一般更新策略是删缓存，这里仅在 put 时也广播一次）
        publisher.publish(name, key);
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        // 先查 L1
        ValueWrapper existing = l1.get(key);
        if (existing != null && existing.get() != null) return existing;

        // 再查 L2
        existing = l2.get(key);
        if (existing != null && existing.get() != null) {
            // 回填 L1
            l1.put(key, existing.get());
            return existing;
        }

        // 双写
        l2.putIfAbsent(key, value);
        l1.putIfAbsent(key, value);

        // 广播失效（提醒其它实例清 L1）
        publisher.publish(name, key);

        return new SimpleValueWrapper(value);
    }

    // ---- EVICT / CLEAR ----

    @Override
    public void evict(Object key) {
        try {
            l2.evict(key); // 先删 L2，防止旧值被回填
        } finally {
            // 本机清 L1
            l1.evict(key);
            // 广播其它节点清 L1
            publisher.publish(name, key);
        }
    }

    @Override
    public void clear() {
        try {
            l2.clear();
        } finally {
            l1.clear();
            publisher.publish(name, null); // cacheName::* 全清
        }
    }

    // ---- 本地失效（供消息订阅方调用）----

    /** 只清本机 L1 某个 key */
    void invalidateLocal(Object key) {
        l1.evict(key);
    }

    /** 只清本机 L1 全部 */
    void invalidateLocalAll() {
        l1.clear();
    }
}